CopyOnWriteArrayList 详解
介绍 CopyOnWriteArrayList 之前,先看一下 ArrayList 的使用:
|
|
执行上面的代码后 jvm 会抛出 ConcurrentModificationException 的异常:
Exception in thread “main” java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)
at java.util.ArrayList$Itr.next(ArrayList.java:859)
at com.example.lib.MyClass.main(MyClass.java:13)
根据报错的堆栈信息可知,异常是在 ArrayList 内部类 Itr 的 checkForComodification 方法抛出的。
反编译 java 代码后,字节码代码如下:
|
|
由字节码可知,foreach 语法糖实际上是利用了迭代器来完成遍历。
先调用 iterator 方法得到迭代器对象
|
|
该方法返回的是 ArrayList 的内部类 Itr 对象。Itr 类代码如下:
|
|
next() 方法会先调用 checkForComodification 方法判断 modCount 与 expectedModCount 是否相等,如果不等的话就抛出异常。
对于 expectedModCount ,当创建 Itr 对象的时候会默认赋值为 modCount。当新增或者删除元素的时候会执行 modCount++ ,所以ArrayList 遍历的时候是不允许新增或者删除元素的,那就无法支持多线程的读写操作了。
java 的并发包 java.util.concurrent 里提供的 CopyOnWriteArrayList 是线程安全的,支持多线程并发读写操作。下面以 JDK1.8 源码分析一下实现原理:
|
|
|
|
|
|
由源码可知,CopyOnWriteArrayList 在新增或者删除元素,先获取可重入锁 ReentrantLock,拷贝原来的数组,在新的数组上面新增或者删除元素。CopyOnWriteArrayList 在读取元素的时候,直接在老数组上读取。CopyOnWriteArrayList 的读写操作是在不同的数组上面操作的,保证了多线程操作的安全性。